package morrior

import (
	"errors"
	"fmt"
	"iot-base/common/fsm"
	"net"
	"strings"
	"sync"
	"sync/atomic"
)

func clientPrepare(pFSM *fsm.FSMInstance) {
	//tip, _ := pFSM.GetContext().GetString("targetIP")
	//tport, _ := pFSM.GetContext().GetInt("targetPort")、*
	/*
		netType, _ := pFSM.GetContext().GetString("netType")

		//cons, _ := pFSM.GetContext().Get("connectionInfos")
		//client则开启一个端口 listen
		if netType == "tcp" {
			listener, err := net.Listen("tcp", "0.0.0.0:0")
			fmt.Println("Listen tcp server init,err:", err)
			if err != nil {

				return
			}
			fmt.Println("now listen addr is :", listener.Addr())
			pFSM.NotifyEvent("prepareOK")
			ptc, _ := pFSM.GetContext().Get("tc")
			if ptc != nil {
				tc := ptc.(*Connection)
				if tc != nil {

					go func(svr net.Listener, tunnelc *Connection) {
						var seq uint32
						seq = 100
						mapConenction := make(map[uint32]*Connection)
						for {
							seq++
							// 建立socket连接

							conn, err := svr.Accept()
							fmt.Println("svr.Accept()", err)

							if err != nil {
								fmt.Println("Listen.Accept failed,err:", err)
								continue
							} else {
								//var buf [200]byte
								clientConn := NewConnection(conn, seq, nil, nil, nil, nil, 60*1)
								mapConenction[seq] = clientConn
								fmt.Println("send to tunnel cmd: ACCEPT ", ACCEPT)
								tc.SendData(0, byte(ACCEPT), nil)

							}

						}
					}(listener, tc)
				}
				return
			}
			pFSM.NotifyEvent("error")
		}*/

}

//BeginClient 启动client
func BeginClient(pwg *sync.WaitGroup, kcpConnection net.Conn) *fsm.FSMInstance {
	//建立状态机
	var connection ClientConnection
	pConnection := NewConnection(kcpConnection, 0,
		(&connection).OnTCData,
		(&connection).onTCCmd,
		(&connection).onTCErr,
		pwg)
	connection.pConn = pConnection
	connection.pFSM = begin(pwg, pConnection)
	connection.pFSM.GetContext().Set("role", "client")
	return connection.pFSM
}

//ClientConnection 客户端连接
type ClientConnection struct {
	pFSM  *fsm.FSMInstance
	pConn *Connection
}

func (pConn *ClientConnection) OnTCData(pPro *MProBody, data []byte, seq uint32) {

	fmt.Println("client*********ondata", len(data), seq)
	if pConn.pFSM != nil {
		id := fmt.Sprintf("connection.%d", seq)
		pClient, _ := pConn.pFSM.GetContext().Get(id)
		if pClient != nil {
			conn, _ := pClient.(net.Conn)
			if conn != nil {
				err := sendful(conn, data)
				if err != nil {
					pConn.pConn.SendData(seq, ERROR_ON_CONNECTION, nil)
					fmt.Println("err send data--------", err)
				}
			}
		} else {
			fmt.Println("client*********not found", id)
		}
	} else {
		fmt.Println("client****pfsm*****not found", seq)
	}
}

func (pConn *ClientConnection) onTCCmd(pPro *MProBody, ctype byte, seq uint32) {
	//id := fmt.Sprintf("cseq.%d", seq)
	id := fmt.Sprintf("connection.%d", seq)
	//fmt.Println("--------oncmd ",pPro.Type)
	fakeConnection, _ := pConn.pConn.pfsm.GetContext().Get(id)
	if fakeConnection != nil {
		switch ctype {
		case ERROR_ON_CONNECTION_RECV, ERROR_ON_CONNECTION_SEND, ERROR_ON_CONNECTION:
			pConn.pConn.pfsm.GetContext().Remove(id)
			closeConection(ctype, fakeConnection)
		}
	}
	fmt.Println("--------oncmd ", ctype, fakeConnection, id)
}

func (pConn *ClientConnection) onTCErr(seq uint32, err error) bool {
	fmt.Println("get error-------kcp---", seq, err)
	//被关闭，此时要通知出现错误
	if pConn.pConn != nil && pConn.pConn.pfsm != nil {
		pConn.pConn.pfsm.NotifyEvent("error")
	}

	return true
}

//MorriorInfo 镜像信息
type MorriorInfo struct {
	TargetIP  string
	NetType   string
	Port      int
	Seq       uint32
	EdgeID    string
	LocalPort int
}

func (pM *MorriorInfo) SetParams(param interface{}) {
	pOther, ok := param.(MorriorInfo)
	if ok {
		*pM = pOther
	} else {
		fmt.Println("&&&&&&&&&&&&&&change type error--------", param)
	}
}
func (pM *MorriorInfo) GetName() string {
	return "mirror"
}

var SEQ uint32

func HasListenner(pM MorriorInfo, pFsm *fsm.FSMInstance) (bool, *net.TCPListener) {
	//jport, _ := pM.Port.Int64()
	strMirror := GetMirrorID(pM.NetType, pM.TargetIP, pM.Port)
	listenner, _ := pFsm.GetContext().Get(strMirror)
	if listenner != nil {
		pListen, _ := listenner.(*net.TCPListener)
		if pListen != nil {
			return true, pListen
		}
	}
	return false, nil
}

func (pM *MorriorInfo) Execute(pFsm *fsm.FSMInstance) error {
	tc, _ := pFsm.GetContext().Get("tc")
	if tc != nil {
		pConnection, _ := tc.(*Connection)
		if pConnection == nil {
			fmt.Println("error mirror ------tc not found--")
			return errors.New("connection is not found")
		} else {
			exists, _ := HasListenner(*pM, pFsm)
			fmt.Println(" mirror ------pm pfsm listernner--", exists)
			if exists {

				return nil
			}
			//jport, _ := pM.Port.Int64()
			if pM.Port == 0 {
				fmt.Println("error mirror ------port is empty--", pM)
				return errors.New("port is empty")
			}

			strMirror := GetMirrorID(pM.NetType, pM.TargetIP, pM.Port)
			//pConnection.Close()
			if pM.NetType != "" {
				go func() {
					//var laddr string
					laddr := fmt.Sprintf("0.0.0.0:%d", pM.LocalPort)
					listener, err := net.Listen(strings.ToLower(pM.NetType), laddr)

					fmt.Println("Listen tcp server init,err********************&&&&&&:", laddr, err)
					if err != nil {
						return
					}
					AddAddr(pFsm, strMirror+"@"+listener.Addr().String())
					pFsm.GetContext().Set(strMirror, listener)
					for {

						//listener.SetReadDeadline(time.Now().Add(time.Minute * time.Duration(3)))
						nClient, err := listener.Accept()
						if err != nil {
							pFsm.GetContext().Remove(strMirror)
							fmt.Println("Listen tcp server Accept,err:", err)
							return
						}
						pM.Seq = atomic.AddUint32(&SEQ, 3)

						pConnection.SendData(pM.Seq, BEGIN_MIRROR, []byte(strMirror))
						fmt.Println("send-----------------data-cmd begin_mirror-", pM.Seq)

						pFsm.GetContext().Set(fmt.Sprintf("connection.%d", pM.Seq), nClient)
						go func(seq uint32) {

							for {
								var buf [1000 * 20]byte
								fmt.Println("recv-----from fake client----", seq)
								rlen, err := nClient.Read(buf[0 : 1000*20])
								fmt.Println("client---------has read---from fake----", rlen, seq)
								if rlen > 0 {
									pConnection.SendData(seq, DATA, buf[0:rlen])
									fmt.Println("client---------send data----to kcp---", seq, rlen)
								} else {
									pConnection.SendData(seq, ERROR_ON_CONNECTION, nil)
									fmt.Println("********socket error----", err, seq, rlen)
									break
								}
							}

						}(pM.Seq)
					}
				}()

			}
		}

	}
	return nil
}
